﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace dz.inner.jky.api
{
    public static class ReflectionUtility
    {
        private static Dictionary<string, Delegate> getAccessors = new Dictionary<string, Delegate>();
        private static Dictionary<string, Delegate> setAccessors = new Dictionary<string, Delegate>();

        /// <summary>
        /// 获取属性的访问器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static Delegate GetPropertyAccessor<T>(T obj, string propertyName)
        {
            string key = obj.GetType().FullName + "." + propertyName;

            if (getAccessors.ContainsKey(key)) return getAccessors[key];

            Type instanceType;
            PropertyInfo propertyInfo;

            ParseProperty<T>(obj, propertyName, out instanceType, out propertyInfo);

            Type delegateType = typeof(Func<,>).MakeGenericType(instanceType, propertyInfo.PropertyType);

            Delegate source = Delegate.CreateDelegate(delegateType, propertyInfo.GetMethod);

            Delegate convert = Delegate.CreateDelegate(delegateType, source.Target, source.Method);

            getAccessors.Add(key, convert);

            return convert;
        }

        /// <summary>
        /// 设置属性的访问器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>

        public static Delegate SetPropertyAccessor<T>(T obj,string propertyName)
        {
            string key = obj.GetType().FullName + "." + propertyName;

            if (setAccessors.ContainsKey(key)) return setAccessors[key];

            Type instanceType;
            PropertyInfo propertyInfo;

            ParseProperty<T>(obj,propertyName, out instanceType, out propertyInfo);

            Type delegateType = typeof(Action<,>).MakeGenericType(instanceType, propertyInfo.PropertyType);

            Delegate source = Delegate.CreateDelegate(delegateType, propertyInfo.SetMethod);

            Delegate convert = Delegate.CreateDelegate(delegateType, source.Target, source.Method);

            setAccessors.Add(key, convert);

            return convert;
        }

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static dynamic GetPropertyValue<T>(T obj, string propertyName)
        {
            Delegate getter = GetPropertyAccessor<T>(obj,propertyName);

            return getter.DynamicInvoke(obj);
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        public static void SetPropertyValue<T1, T2>(T1 obj, string propertyName, T2 value)
        {
            Delegate setter = SetPropertyAccessor<T1>(obj,propertyName);

            try
            {
                setter.DynamicInvoke(new object[] { obj, value });
            }
            catch (Exception ex)
            {

                throw new Exception($"设置属性失败：{propertyName}: {ex.Message}");
            }
            
        }

        

        private static void ParseProperty<T>(T obj, string propertyName, out Type instanceType, out PropertyInfo propertyInfo)
        {
            instanceType = obj.GetType();

            propertyInfo = instanceType.GetProperty(propertyName);

            if (propertyInfo == null) throw new Exception($"类型{instanceType.FullName} 中未找到属性 {propertyName}");
        }
    }
}
